﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Net.Sockets;
using System.Threading;
using System.IO;
using System.Net;
using TestTcpSockets.Plugin.Interface;
using TestTcpSockets.Plugin.System;
using TestTcpSockets.Xml;
using agsXMPP.Xml;
using agsXMPP.protocol.sasl;
using NabNetLib;
using NabNetLib.Clapier;
using System.Security.Cryptography;
using NabNetLib.Remoting;
using System.Reflection;


namespace TestTcpSockets
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        const int LIMIT = 1; //5 concurrent clients
        //private static TcpListener listener1;
        private static TcpListener listener2;
        private static TcpListener listener3;

        private static List<INabPlugin> plugins;
        

        private static TextBox logBox;

        private void button1_Click(object sender, EventArgs e)
        {
            //listener1 = new TcpListener(IPAddress.Any, 5222);
            //listener1.Start();

            listener2 = new TcpListener(IPAddress.Any, 8080);
            listener2.Start();

            RemoteNabNet.RunRemotingServer();

            //listener3 = new TcpListener(IPAddress.Any, 80);
            //listener3.Start();

            plugins = new List<INabPlugin>();
            plugins.Add(new Boot());
            plugins.Add(new Locate());
            plugins.Add(new Record());

            logBox = this.textBox1;

            Log("Server mounted, listening to port 5222");
            ServiceXMPP();

            for (int i = 0; i < LIMIT; i++)
            {
                //Thread t1 = new Thread(new ThreadStart(ServiceXMPP));
                //t1.Start();

                //Thread t2 = new Thread(new ThreadStart(ServiceHTTP1));
                //t2.Start();

                Thread t3 = new Thread(new ThreadStart(ServiceHTTP2));
                t3.Start();
            }
        }

        internal static void Log(string text)
        {
            if (logBox.InvokeRequired)
            {
                logBox.Invoke((MethodInvoker)delegate {
                    logBox.AppendText(text + Environment.NewLine);
                });
            }
            else
            {
                logBox.AppendText(text + Environment.NewLine);
            }
        }


        public static void ServiceXMPP()
        {
            //while (true)
            {
                //Socket soc = listener1.AcceptSocket();


                //Log(string.Format("ConnectedXMPP: {0}", soc.RemoteEndPoint));
                Log("Xmpp listener started");
                
                try
                {
                    using (nabnetEntities objectContext = new nabnetEntities())
                    {
                        XmppListener mListener = new XmppListener();
                        Scheduler scheduler = new Scheduler();
                        scheduler.StartScheduler();

                        Thread t = new Thread(mListener.Main);
                        t.Start();
                        //XmppServerConnection server = new XmppServerConnection(soc, objectContext);
                    }

                    //if (false)
                    //{
                    //    NetworkStream s = new NetworkStream(soc);

                    //    Authentication mAuthPlugin = new Authentication();
                    //    int currentAuthStep = 0;

                    //    while (soc.IsConnected())
                    //    {


                    //        if (s.DataAvailable)
                    //        {
                    //            byte[] buffer = new byte[1000];
                    //            s.Read(buffer, 0, 1000);

                    //            string request = Encoding.UTF8.GetString(buffer);


                    //            //request.Append(sr.ReadToEnd());
                    //            //while (true)
                    //            //{
                    //            //    Log("ping");
                    //            //    string line = sr.ReadLine();
                    //            //    Log("pong");
                    //            //    if (string.IsNullOrEmpty(line)) break;
                    //            //    request.Append(line + Environment.NewLine);
                    //            //}
                    //            Log("<--from nab-->");
                    //            Log(request);

                    //            if (currentAuthStep != 5)
                    //            {
                    //                StringBuilder answer = new StringBuilder();

                    //                if (mAuthPlugin.DoAuth(ref currentAuthStep, request, answer))
                    //                {
                    //                    Log("<--to nab-->");
                    //                    Log(answer.ToString());

                    //                    WriteToXmppSocket(s, answer.ToString());

                    //                }
                    //                else
                    //                {
                    //                    break;
                    //                }
                    //            }
                    //            else
                    //            {
                    //                //nab authenticated
                    //                IQ iq = IQ.Deserialize(request);
                    //                if (iq != null)
                    //                {
                    //                    if (iq.Binding != null)
                    //                    {
                    //                        IQ rIq = new IQ()
                    //                                     {
                    //                                         IqType = "result",
                    //                                         Identifier = iq.Identifier
                    //                                     };
                    //                        rIq.Binding = new Bind()
                    //                                          {
                    //                                              Namespace = "urn:ietf:params:xml:ns:xmpp-bind",
                    //                                              Jid = "0019db01f416@artiche.pck.nerim.net/boot"
                    //                                          };

                    //                        string answer = rIq.Serialize();
                    //                        Log(answer);
                    //                        WriteToXmppSocket(s, answer);

                    //                    }
                    //                }
                    //            }
                    //        }

                    //        //sw.WriteLine("plop");
                    //    }
                    //    s.Close();
                    //}
                }
                catch (Exception e)
                {
                    Log(e.ToString());
                }

                //Log(string.Format("Xmpp listeDisconnected: {0}", soc.RemoteEndPoint));

                //Log(string.Format("Disconnected: {0}", soc.RemoteEndPoint));
                //soc.Close();
            }
        }

        private static void WriteToXmppSocket(NetworkStream s,string answer)
        {
            byte[] bufferResponse = Encoding.UTF8.GetBytes(answer);
            s.Write(bufferResponse, 0, bufferResponse.Length);
            s.Flush();
        }

        public static void ServiceHTTP1()
        {
            while (true)
            {
                Socket soc = listener3.AcceptSocket();
                //soc.SetSocketOption(SocketOptionLevel.Socket,
                //        SocketOptionName.ReceiveTimeout,10000);

                Log(string.Format("ConnectedHTTP:80: {0}", soc.RemoteEndPoint));

                try
                {
                    using (NetworkStream s = new NetworkStream(soc))
                    {
                        
                        StreamWriter sw = new StreamWriter(s);
                        sw.AutoFlush = true; // enable automatic flushing

                        StringBuilder request = new StringBuilder();

                        List<byte> rawRequest = new List<byte>();

                        do
                        {
                            byte[] buffer = new byte[1000];
                            int cpt = s.Read(buffer, 0, 1000);
                            ArraySegment<byte> plop = new ArraySegment<byte>(buffer,0,cpt);
                            rawRequest.AddRange(plop.Array);
                            request.Append(Encoding.UTF8.GetString(plop.Array));
                        } while (s.DataAvailable);
                        request.Replace("\0", "");
                        Log(request.ToString());

                        //while (true)
                        //{
                        //    string line = sr.ReadLine();
                        //    if (string.IsNullOrEmpty(line)) break;
                        //    Log(line);
                        //    request.Append(line + Environment.NewLine);
                        //    //sw.WriteLine("plop");
                        //}

                        using (MemoryStream reply = new MemoryStream())
                        {
                            foreach (INabPlugin plugin in plugins)
                            {
                                if (plugin.HandleRequest(rawRequest.ToArray(),request.ToString(), reply))
                                {
                                    break;
                                }
                            }
                            reply.Position = 0;
                            byte[] b = new byte[reply.Length];
                            reply.Read(b, 0, (int) reply.Length);

                            sw.Write("HTTP/1.1 200 OK\r\n");
                            sw.Write("Date: Wed, 27 Jul 2011 23:19:13 GMT\r\n");
                            sw.Write("Server: Apache/2.2.9 (Debian) PHP/5.2.6-1+lenny10 with Suhosin-Patch\r\n");
                            sw.Write("X-Powered-By: PHP/5.2.6-1+lenny10\r\n");
                            sw.Write("Vary: Accept-Encoding\r\n");
                            sw.Write("Content-Encoding: gzip\r\n");
                            sw.Write("Content-Length: 40016\r\n");
                            sw.Write("Keep-Alive: timeout=15, max=100\r\n");
                            sw.Write("Connection: Keep-Alive\r\n");
                            sw.Write("Content-Type: text/html\r\n\r\n");
                            s.Write(b, 0, b.Length);
                        }

                        

                    }
                }
                catch (Exception e)
                {
                    Log(e.ToString());
                }

                Log(string.Format("DisconnectedHTTP1: {0}", soc.RemoteEndPoint));
                soc.Close();
            }
        }

        public static void ServiceHTTP2()
        {
            while (true)
            {
                Socket soc = listener2.AcceptSocket();
                //soc.SetSocketOption(SocketOptionLevel.Socket,
                //        SocketOptionName.ReceiveTimeout,10000);

                Log(string.Format("ConnectedHTTP2: {0}", soc.RemoteEndPoint));

                try
                {
                    Log("HTTP2");
                    Stream s = new NetworkStream(soc);
                    StreamReader sr = new StreamReader(s);
                    StreamWriter sw = new StreamWriter(s);
                    sw.AutoFlush = true; // enable automatic flushing
                    Log("HTTP2");

                    while (true)
                    {
                        Log("HTTP2");
                        string name = sr.ReadLine();
                        if (string.IsNullOrEmpty(name)) break;
                        Log(name);
                        //sw.WriteLine("plop");
                    }
                    s.Close();
                }
                catch (Exception e)
                {
                    Log(e.Message);
                }

                Log(string.Format("DisconnectedHTTP2: {0}", soc.RemoteEndPoint));
                soc.Close();
            }
        }

        private void button2_Click(object sender, EventArgs e)
        {
            agsXMPP.protocol.Base.Stream stream = new agsXMPP.protocol.Base.Stream()
            {
                Id = "2173750751",
                From = new agsXMPP.Jid("artiche.pck.nerim.net")
            };

            Mechanisms mechanisms = new Mechanisms();
            mechanisms.AddChild(new Mechanism() { Value = "DIGEST-MD5" });
            mechanisms.AddChild(new Mechanism() { Value = "PLAIN" });
            stream.AddChild(mechanisms);

            string f = stream.ToString();

            StreamParser p = new StreamParser();
            p.OnStreamElement += new StreamHandler(p_OnStreamElement);
            byte[] b = Encoding.UTF8.GetBytes("<iq type='get' id='1'><query xmlns='violet:iq:register'/></iq>");
            p.Push(b,0,b.Length);

            
            agsXMPP.protocol.iq.register.RegisterIq plop = new agsXMPP.protocol.iq.register.RegisterIq(agsXMPP.protocol.client.IqType.get);
            plop.Id = "1";
            string h = plop.ToString();

            agsXMPP.protocol.iq.register.Register plip = new agsXMPP.protocol.iq.register.Register("user","pass");
            string k = plop.ToString();
        }

        void p_OnStreamElement(object sender, agsXMPP.Xml.Dom.Node e)
        {
            string g = e.ToString();
        }

        private void button3_Click(object sender, EventArgs e)
        {
            //Type moodPlugin = Type.GetType("NabNetLib.Apps.Mood.MoodPlugin, NabNetLib, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null");

            //Type tptp = typeof(NabNetLib.Apps.Mood.MoodPlugin);

            using (nabnetEntities objectContext = new nabnetEntities())
            {
                Lapin mLapin = LapinManager.GetLapin(objectContext, "0019db01f416");

                textBox1.AppendText(mLapin.Lapin2Plugin.Count.ToString());
                if (mLapin == null)
                {

                    mLapin = new Lapin()
                                 {
                                     serial_number = "toto",
                                     name = "no name"
                                 };
                    objectContext.Lapin.AddObject(mLapin);
                    objectContext.SaveChanges();


                }


                mLapin.password = "totoz";

                objectContext.SaveChanges();
            }

        }


        private string ComputeMd5Hash(string username, string password, string nonce, string cnonce, string nc, string digestUri, string mode)
        {
            MD5 md5HashAlgo = MD5.Create();

            List<byte> tmpHash = new List<byte>();
            tmpHash.AddRange(md5HashAlgo.ComputeHash(Encoding.UTF8.GetBytes(username + "::" +password)));
            tmpHash.AddRange(Encoding.UTF8.GetBytes(":" + nonce + ":" + cnonce));

            byte[] tmpBufferHash = md5HashAlgo.ComputeHash(tmpHash.ToArray());
            StringBuilder HA1 = new StringBuilder();
            for (int i = 0; i < tmpBufferHash.Length; i++)
            {
                // Affiche le Hash en hexadecimal 
                HA1.Append(tmpBufferHash[i].ToString("x2"));
            }


            tmpBufferHash = md5HashAlgo.ComputeHash(Encoding.UTF8.GetBytes(mode+":" + digestUri));
            StringBuilder HA2 = new StringBuilder();
            for (int i = 0; i < tmpBufferHash.Length; i++)
            {
                // Affiche le Hash en hexadecimal 
                HA2.Append(tmpBufferHash[i].ToString("x2"));
            }

            tmpBufferHash = md5HashAlgo.ComputeHash(
                Encoding.UTF8.GetBytes(HA1 + ":" + nonce + ":" + nc + ":" + cnonce + ":auth:" + HA2)
            );
            StringBuilder finalMd5Hash = new StringBuilder();
            for (int i = 0; i < tmpBufferHash.Length; i++)
            {
                // Affiche le Hash en hexadecimal 
                finalMd5Hash.Append(tmpBufferHash[i].ToString("x2"));
            }

            return finalMd5Hash.ToString();
        }

    }

    static class SocketExtensions
    {
        public static bool IsConnected(this Socket socket)
        {
            try
            {
                return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
            }
            catch (SocketException) { return false; }
        }
    }
}
